const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`) //匹配 开头的正则 捕获内容是标签名
const startTagClose = /^\s*(\/?)>/  //匹配标签结束的 >
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) //匹配标签结尾的 </div>
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g

import { generate } from './generate'



function parseHTML(html) {

    function createAstTree(tag, attrs) {
        // 默认都是 元素结点，所以 type 是 1，文本结点会单独判断
        return {
            tag,
            type: 1,
            attrs,
            children: [],
            parent: null
        }
    }

    // 删除 html的部分结构
    function advance(n) {
        html = html.substring(n)
    }
    // ast 主体
    let root
    // 保存当前的父元素，为后续实现双向链表 [div,span]
    let currentParent
    // 栈结构
    const stack = []

    // 开始标签函数
    function start(tag, attr) {
        const element = createAstTree(tag, attr)
        if (!root) {
            root = element
        }
        // 这边将对象的内存地址 赋值给 currentParent，currentParent改变，原对象也会改变，所以上面的 root 其实就是最终返回的 root
        currentParent = element
        stack.push(element)
        // console.log(tag, attr, '-------开始');
    }

    // 结束标签函数
    function end(tag) {
        // 实现双向链表结构
        const lastEle = stack.pop()
        currentParent = stack[stack.length - 1]
        if (currentParent) {
            lastEle.parent = currentParent;
            currentParent.children.push(lastEle)
        }
        // console.log(tag, '-------结束');
    }

    // 文本 函数
    function chars(text) {
        text = text && text.trim();
        currentParent.children.push({
            type: 3,
            text
        })
        // console.log(text, '-----文本');
    }

    // 当 html 还未删除完 就一直执行
    while (html) {
        let textEnd = html.indexOf('<');;
        if (textEnd === 0) {  //肯定是标签
            // 是开始标签的情况时
            const startMatch = parseStartTag()
            if (startMatch) {
                // 开始标签的函数执行
                start(startMatch.tag, startMatch.attrs);
                continue;
            }

            // 是结束标签的情况时
            const endMatch = html.match(endTag);
            if (endMatch) {
                advance(endMatch[0].length);
                // 结束标签的函数执行
                end(endMatch[1]);
                continue;
            }
        }
        let text
        if (textEnd > 0) { // 文本
            text = html.substring(0, textEnd)
        }
        if (text) {
            advance(text.length);
            // 文本的函数执行
            chars(text);
        }

    }


    function parseStartTag() {
        // 匹配开始标签
        const start = html.match(startTagOpen);
        if (start) {
            // 设置 属性对象
            const match = {
                tag: start[1],
                attrs: []
            }
            advance(start[0].length)
            let end
            let attr
            // 将属性 一直循环到 结束标签出现
            while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
                match.attrs.push({
                    name: attr[1],
                    // 当 属性的 value 是用 单引号 或者 没有引号时，就在 4和5的位置
                    value: attr[3] || attr[4] || attr[5]
                })
                advance(attr[0].length)
            }
            // 结束标签 > 出现
            if (end) {
                advance(end[0].length);
                return match;
            }
        }
    }
    return root
}

export function compileToFunction(template) {
    // 获得 ast 语法树
    const ast = parseHTML(template)
    // 将 语法树 生成 _c(_v,_s) 这种格式
    const code = generate(ast)
    // 生成 render 函数，通过 with 来获取 this 上的数据
    const render = new Function(`with(this){return ${code}}`)
    return render
}


